#include "libpocketj.hpp"
#include <fstream>
#include <fcgi_stdio.h>

class CGIManager {

public:
  CGIManager(std::string configName) : confPath(configName){}

  ~CGIManager() {}

  void openAnalyzer()
  {
    readConfig();
    anal = new pocketj::Analyzer(T, P, DB_PATH);
  }

  void closeAnalyzer()
  {
    delete anal;
  }

  const unsigned int getLifeTime()
  {
    return FCGLIFETIME;
  }

  int accept()
  {
    return FCGI_Accept();
  }

private:
  double T;
  double P;
  std::string DB_PATH;
  std::string confPath;
  pocketj::Analyzer* anal;
  enum {
    FCGLIFETIME = 10000,
    MINIBNUM    = 31,
    UPLOADMAX   = (256*1024*1024),
  };

  bool readConfig()
  {
    std::ifstream ifs(confPath.c_str(), std::ios::in);
    if(!ifs) return false;
    std::string line;
    while (std::getline(ifs, line)) {
      std::vector<std::string> params = pocketj::util::split(line, "\t");
      if (params[0] == "threshold") {
        T = std::atof(params[1].c_str());
      } else if (params[0] == "parameter") {
        P = std::atof(params[1].c_str());
      } else {
        return false;
      }
    }
    return true;
  }

  /* dispatch the acctual process by mode */
  void dispatch()
  {
    TCMAP* params = tcmapnew2(MINIBNUM);
    readparameters(params);
  }


  /* read CGI parameters */
  void readparameters(TCMAP* params)
  {
    char* buf  = NULL;
    int len    = 0;
    const char* rp;
    if((rp = getenv("REQUEST_METHOD")) != NULL && !strcmp(rp, "POST") &&
       (rp = getenv("CONTENT_LENGTH")) != NULL && (len = atoi(rp)) > 0){
      if(len > UPLOADMAX) len = UPLOADMAX;
      buf = static_cast<char*>(tccalloc(len + 1, 1));
      if(static_cast<int>(fread(buf, 1, len, stdin)) != len){
        tcfree(buf);
        buf = NULL;
      }
    } else if((rp = getenv("QUERY_STRING")) != NULL){
      buf = tcstrdup(rp);
      len = strlen(buf);
    }
    if(buf && len > 0){
      if((rp = getenv("CONTENT_TYPE")) != NULL
         && tcstrfwm(rp, "multipart/form-data")
         && (rp = strstr(rp, "boundary=")) != NULL){
        rp += 9;
        if(*rp == '"') rp++;
        char bstr[strlen(rp)+1];
        strcpy(bstr, rp);
        char* wp = strchr(bstr, ';');
        if(wp) *wp = '\0';
        wp = strchr(bstr, '"');
        if(wp) *wp = '\0';
        TCLIST* parts = tcmimeparts(buf, len, bstr);
        int pnum      = tclistnum(parts);
        for(int i = 0; i < pnum; i++){
          int psiz;
          const char* part =
            static_cast<const char*>(tclistval(parts, i, &psiz));
          TCMAP* hmap = tcmapnew2(MINIBNUM);
          int bsiz;
          char* body = tcmimebreak(part, psiz, hmap, &bsiz);
          int nsiz;
          const char* name =
            static_cast<const char*>(tcmapget(hmap, "NAME", 4, &nsiz));
          if(name){
            tcmapput(params, name, nsiz, body, bsiz);
            const char* fname = tcmapget2(hmap, "FILENAME");
            if(fname){
              if(*fname == '/'){
                fname = strrchr(fname, '/') + 1;
              } else if(((*fname >= 'a' && *fname <= 'z') ||
                         (*fname >= 'A' && *fname <= 'Z')) &&
                        fname[1] == ':' && fname[2] == '\\'){
                fname = strrchr(fname, '\\') + 1;
              }
              if(*fname != '\0'){
                char key[nsiz+10];
                sprintf(key, "%s_filename", name);
                tcmapput2(params, key, fname);
              }
            }
          }
          tcfree(body);
          tcmapdel(hmap);
        }
        tclistdel(parts);
      } else {
        TCLIST* pairs = tcstrsplit(buf, "&");
        int num = tclistnum(pairs);
        for(int i = 0; i < num; i++){
          char* key = tcstrdup(tclistval2(pairs, i));
          char* val = strchr(key, '=');
          if(val){
            *(val++)   = '\0';
            char* dkey = tcurldecode(key, &len);
            char* dval = tcurldecode(val, &len);
            tcmapput2(params, dkey, dval);
            tcfree(dval);
            tcfree(dkey);
          }
          tcfree(key);
        }
        tclistdel(pairs);
      }
    }
    tcfree(buf);
  }
};

int main(int argc, char** argv)
{
  unsigned int cnt = 0;
  std::string configName = "./pocketj.conf";
  CGIManager cgiMgr(configName);
  return 0;
}
